Users set Keras layers(build Layers)

케라스가 제공하지 않는 층을 새로 정의할 때, tf.keras.layers.Layer 클래스를 상속하여 새로운 클라스를 정의한다.
새로운 층 제작 혹은 기존 층을 커스터마이징

새로운 클래스에서는 __init__() 메서드와 call() 메서드를 정의해 주어야 한다.
생성자(__init__)에서 층에서 필요한 변수와 텐서를 정의한다.
생성자에 input_shape 매개변수가 있으면, 변수를 만들고 초기화 할 수 있다.
(정확한 입력 크기를 알 수 없다면, build를 통해 지연 지정해주면 된다.

get_config() 메서드를 정의하여 직렬화 할 수 있다.(텐서플로 모델의 저장과 복원 기능을 사용하여 효율적으로 저장)
wx+b에서 선형층 계산을 w(x+e)+b로 정의(e는 잡음을 위한 랜덤 변수)
class NoisyLinear(tf.keras.layers.Layer):
def __init__(self, output_dim, noise_stddev=0.1, **kwargs):
self.output_dim=output_dim
self.noise_stddev=noise_stddev
super(NoisyLinear, self).__init__(*kwargs)
def build(self, input_shape):
self.w=self.add_weight(name='weights', shape=(input_shape[1], self.output_dim), initializer='random_normal', trainable=True)
self.b=self.add_weight(shape=(self.output_dim,), initializer='zeros', trainable=True)
def call(self, inputs, training=False):
if training:
batch=tf.shape(inputs)[0]
dim=tf.shape(inputs)[1]
noise=tf.random.normal(shape=(batch, dim), mean=0.0, stddev=self.noise_stddev)
noisy_inputs=tf.add(inputs, noise)
else:
noisy_inputs=inputs
z=tf.matmul(noisy_inputs, self.w)+self.b
return tf.keras.activations.relu(z)
def get_config(self):
config=super(NoisyLinear, self).get_config()
config.update({'output_dim':self.output_dim, 'noise_stddev':self.noise_stddev})
return config
NoisyLinear의 생성자에 추가한 noise_stddev는 잡음을 위한 노이즈 변수의 표준 편차이다.
tf.random.normal 가우시안 분포에서 샘플링 된다.

call() 메서드의 training 매개변수를 False로 지정(모델이 훈련에서 사용되는지, 예측에서만 사용되는지 지정)
위 코드는 훈련할 때만 랜덤 벡터 e를 생성하고 추론이나 예측할 때는 사용하지 않는다.
Dropout과 같은 기능은 훈련과 예측에서 다르게 동작한다.

훈련에는 그레이디언트가 필요하지만 예측에는 그레이디언트가 필요없음
tf.random.set_seed(1)
noisy_layer=NoisyLinear(4)
noisy_layer.build(input_shape=(None, 4))
x=tf.zeros(shape=(1, 4))
tf.print(noisy_layer(x, training=True))

[[0 0.00821428 0 0]]

설정(config)을 이용한 모델 만들기
config=noisy_layer.get_config()
new_layer=NoisyLinear.from_config(config)
tf.print(new_layer(x, training=True)

TypeError: Cannot convert value 'dtype' to a TensorFlow DType.


변수에러 발생
tf.keras.layers.Layer을 상속받은 클래스의 from_config() 메서드에게
get_config()를 통해 얻은 config를 대입함으로써, 동일한 층을 만들 수 있다.
XOR Classified(with Users set Layers)
Sequential를 통해서 Users defined Layers 사용
tf.random.set_seed(1)
model=tf.keras.Sequential([
NoisyLinear(4, noise_stddev=0.1),
tf.keras.layers.Dense(units=4, activation='relu'),
tf.keras.layers.Dense(units=4, activation='relu'),
tf.keras.layers.Dense(units=1, activation='sigmoid')])
model.build(input_shape=(None, 2))
model.summary()

Model: "sequential_1"

_________________________________________________________________

Layer (type)                 Output Shape              Param #   

=================================================================

noisy_linear_4 (NoisyLinear) (None, 4)                 12        

_________________________________________________________________

dense_3 (Dense)              (None, 4)                 20        

_________________________________________________________________

dense_4 (Dense)              (None, 4)                 20        

_________________________________________________________________

dense_5 (Dense)              (None, 1)                 5         

=================================================================

Total params: 57

Trainable params: 57

Non-trainable params: 0

_________________________________________________________________

compile & fit
model.compile(optimizer=tf.keras.optimizers.SGD(),
loss=tf.keras.losses.BinaryCrossentropy(),
metrics=[tf.keras.metrics.BinaryAccuracy()])
hist=model.fit(x_train, y_train, validation_data=(x_valid, y_valid), epochs=200, batch_size=2, verbose=0)
graph
import matplotlib.pyplot as plt
from mlxtend.plotting import plot_decision_regions
history=hist.history
fig=plt.figure(figsize=(16 ,4))
ax=fig.add_subplot(1, 3, 1)
plt.plot(history['loss'], lw=4)
plt.plot(history['val_loss'], lw=4)
plt.legend(['Train loss', 'Validation loss'], fontsize=15)
ax.set_xlabel('Epochs', size=15)
ax=fig.add_subplot(1, 3, 2)
plt.plot(history['binary_accuracy'], lw=4)
plt.plot(history['val_binary_accuracy'], lw=4)
plt.legend(['Train Acc.', 'Validation Acc.'], fontsize=15)
ax.set_xlabel('Epochs', size=15)
ax=fig.add_subplot(1, 3, 3)
plot_decision_regions(X=x_valid, y=y_valid.astype(np.integer), clf=model)
ax.set_xlabel(r'$x_1$', size=15)
ax.xaxis.set_label_coords(1, -0.025)
ax.set_ylabel(r'$x_2$', size=15)
ax.yaxis.set_label_coords(-0.025, 1)
plt.show()